S.Ct.Meta = function (div) {
    let me = this;
    Q.doSuperConstructor(me, S.Ct.Meta, [div]);
};

S.Ct.Meta.prototype = {
    xtype: 'meta',
    colors: ['#39b3d7', '#ff0000', '#00ff00', '#0000ff'],
    node: {
        label: {
            size: {
                width: 230,
                height: 28
            }
        },
        padding: {
            top: 10,
            left: 8
        }
    },

    onClick: function (evt) {
        let node = evt.getData();
        if (node && node.xtype == 'expander') {
            node.onClick(evt);
        }
    },

    onDblClick: function (evt) {
        let node = evt.getData();
        if (node && node.xtype == 'instance') {
            node.onDblClick(evt);
        }
    },

    createExpander: function (node) {
        let me = this, styles = node.styles, expander = new S.Node.Expander({
            x: node.x + styles['label.size'].width - styles['label.padding'].left - 5,
            y: node.y - 3,
            zIndex: node.zIndex
        });
        expander.graph = me;
        me.graphModel.add(expander);
        expander.host = expander.parent = node;
        return expander;
    },

    createInstance: function (data, x, y, z, size, colors, movable) {
        let me = this, instance = new S.Node.Instance({
            zIndex: z || 1,
            name: data.name,
            movable: movable,
            x: x,
            y: y,
            style: {
                label: {
                    color: colors.font,
                    size: {
                        width: size.width,
                        height: size.height
                    }
                },
                border: {
                    width: 1,
                    color: colors.border
                },
                background: {
                    color: colors.background
                }
            }
        });
        instance.graph = me;
        instance.setData(data);
        me.graphModel.add(instance);
        return instance;
    },

    createFrame: function (node) {
        node.removeFrame();
        let me = this, styles = node.styles, colors = node.colors(), frame = new S.Node.Frame({
            x: node.x - node.size.width + styles['padding'].left / 2,
            y: node.y,
            zIndex: node.zIndex,
            style: {
                shape: {
                    stroke: {
                        style: colors.border
                    }
                }
            }
        });
        let w = node.size.width + styles['label.size'].width - styles['padding'].left;
        let bottom = node.bottom();
        let h = bottom.y - node.y + styles['label.size'].height / 2 + frame.padding.bottom;
        frame.lineTo(0, h);
        frame.lineTo(w, h);
        frame.lineTo(w, 0);
        frame.graph = me;
        me.graphModel.add(frame);
        frame.host = frame.parent = node;
        return frame;
    },

    createAllEdges: function () {
        let me = this;
        me.graphModel.forEachNode((node) => {
            if (node.xtype == 'instance') {
                me.createEdges(node.data.relations, node.data.children);
            }
        });
    },

    createEdges: function (relations, children) {
        let me = this;
        if (relations && relations.length > 0) { // 节点的连线
            for (let relation of relations) {
                me.createEdge(relation.from, relation.to)
            }
        }
        if (children && children.length > 0) { // 子节点的连线
            for (let child of children) {
                me.createEdges(child.relations, child.children);
            }
        }
    },

    createEdge: function (fromPath, toPath) {
        let me = this, gm = me.graphModel;
        let from = me.findByPath(fromPath.path), to = me.findByPath(toPath.path);
        if (from && to && !from.existedEdge(to.id)) {
            let solid = (from.data.id == fromPath.id) && (to.data.id == toPath.id), colors = to.colors();
            let edge = new S.Edge.Relation({
                tooltip: from.name + '(' + from.data.id + ') > ' + to.name + '(' + to.data.id + ')',
                from: from,
                to: to,
                style: {
                    arrow: {
                        from: {
                            fill: {
                                color: colors.border
                            }
                        }
                    },
                    edge: {
                        line: {
                            dash: solid ? null : [2, 2]
                        },
                        color: colors.border
                    }
                }
            });
            edge.graph = me;
            edge.fromId = fromPath.id;
            edge.toId = toPath.id;
            me.graphModel.add(edge);
            return edge;
        }
    },

    reverseColors: function (colors) {
        let result = {
            border: colors.border
        };
        result.font = colors.background;
        result.background = colors.font;
        return result;
    },

    createNode: function (data, x, y, z, color) {
        let me = this, colors = {
            font: '#fff',
            border: color,
            background: color
        };
        let instance = me.createInstance(data, x, y, z, me.node.label.size, colors, true);
        if (data.children && data.children.length > 0) {
            let expander = me.createExpander(instance);
            expander.setExpanded(true);
        }
        return instance;
    },

    createChildren: function (parent, padding, reverse) {
        let me = this, children = parent.data.children, colors = parent.colors();
        let styles = parent.styles, height = styles['label.size'].height, instance;
        for (let child of children) {
            instance = me.createInstance(child,
                parent.x + padding.left, (instance ? instance.y : parent.y) + height + padding.top, parent.zIndex - 1, {
                    width: styles['label.size'].width - padding.left * 2,
                    height: height
                },
                reverse ? me.reverseColors(colors) : colors, false);
            instance.host = instance.parent = parent;
            // 下一级，最多2级
            if (child.children && child.children.length > 0) {
                let expander = me.createExpander(instance);
                child.expanded && expander.setExpanded(true);
            }
        }
    },

    createLegend: function (legend) {
        let me = this, i = 0;
        for (let p in legend) {
            legend[p].color = me.colors[i] || me.colors[0];
            i++;
        }
    },

    setData(data) {
        let me = this, x, y, z, instance, legend = data.legend, columns = data.data;
        me.data = data;
        x = y = z = 0;
        me.createLegend(legend);
        for (let column of columns) {
            for (let node of column) {
                instance = me.createNode(node, x, y, z, legend[node.classifier].color);
                y = instance.bottom().y + instance.styles['label.size'].height + 40;
                z -= 3;
            }
            x += instance.styles['label.size'].width + 120;
            y = 0;
        }
        me.createAllEdges();
    }

};

Q.extend(S.Ct.Meta, S.Ct.Container);